Makefile 用于帮助决定大型程序的哪些部分需要重新编译。在绝大多数情况下,编译 C 或 C++ 文件。其他语言通常有自己的工具,其用途与 Make 相似。当你需要一系列指令来运行取决于哪些文件已更改时,Make 也可以在编译之外使用。本教程将重点介绍 C/C++ 编译用例。
这是你可以使用 Make 构建的示例依赖关系图。如果任何文件的依赖项发生更改,则该文件将被重新编译:
Make 有哪些替代方案?流行的 C/C++ 替代构建系统是SCons、CMake、Bazel和Ninja。一些代码编辑器(如Microsoft Visual Studio)有自己的内置构建工具。对于 Java,有Ant、Maven和Gradle。Go 和 Rust 等其他语言有自己的构建工具。
Python、Ruby 和 Javascript 等解释型语言不需要类似于 Makefile。Makefiles 的目标是根据已更改的文件来编译需要编译的任何文件。但是当解释语言中的文件发生变化时,不需要重新编译任何东西。当程序运行时,将使用该文件的最新版本。
Make的版本和类型Make 有多种实现方式,但本指南的大部分内容都适用于你使用的任何版本。但是,它是专门为 GNU Make 编写的,它是 Linux 和 MacOS 上的标准实现。所有示例都适用于 Make 版本 3 和 4,除了一些深奥的差异之外,它们几乎相同。
运行示例要运行这些示例,你需要一个终端并安装“make”。对于每个示例,将内容放在一个名为 的文件Makefile中,然后在该目录中运行命令make。让我们从最简单的 Makefile 开始:
hello:echo "Hello, World"注意:Makefile必须使用TAB而不是空格缩进,否则make会失败。
这是运行上述示例的输出:
$ makeecho "Hello, World"Hello, World而已!如果你有点困惑,这里有一个视频,介绍了这些步骤,并描述了 Makefile 的基本结构。
生成文件语法一个 Makefile 由一组规则组成。规则通常如下所示:
targets: prerequisitescommandcommandcommand目标是文件名,以空格分隔。通常,每条规则只有一个。这些命令是通常用于制作目标的一系列步骤。这些需要以制表符开头,而不是空格。先决条件也是文件名,以空格分隔。这些文件需要在运行目标命令之前存在。这些也称为依赖项Make的精髓让我们从一个 hello world 示例开始:
hello:echo "Hello, World"echo "This line will always print, because the file hello does not exist."这里已经有很多东西可以吸收了。让我们分解一下:
我们有一个目标叫做hello这个目标有两个命令此目标没有先决条件然后我们将运行make hello. 只要hello文件不存在,命令就会运行。如果hello确实存在,则不会运行任何命令。
重要的是要意识到我说hello的是target和file。那是因为两者是直接联系在一起的。通常,当运行目标时(也就是运行目标的命令时),这些命令将创建一个与目标同名的文件。在这种情况下,hello 目标不会创建hello 文件。
让我们创建一个更典型的 Makefile - 一个编译单个 C 文件的文件。但在我们这样做之前,请创建一个名为的文件,该文件blah.c具有以下内容:
// blah.cint main() { return 0; }然后创建 Makefile(Makefile一如既往地称为):
blah:cc blah.c -o blah这一次,尝试简单地运行make. 由于没有提供目标作为make命令的参数,因此运行第一个目标。在这种情况下,只有一个目标 (blah)。第一次运行时,blah将创建。第二次,你会看到make: 'blah' is up to date。那是因为该blah文件已经存在。但是有一个问题:如果我们修改blah.c然后运行make,则不会重新编译。
我们通过添加一个先决条件来解决这个问题:
blah: blah.ccc blah.c -o blah当我们make再次运行时,会发生以下一组步骤:
选择第一个目标,因为第一个目标是默认目标这有一个先决条件blah.cMake 决定它是否应该运行blah目标。它只会在blah不存在或更新blah.c时运行 blah最后一步很关键,也是make 的精髓。它试图做的是确定blah自blah上次编译以来是否发生了先决条件。即如果blah.c被修改,运行make应该重新编译文件。反之,如果blah.c没有改变,则不应重新编译。
为了实现这一点,它使用文件系统时间戳作为代理来确定是否发生了变化。这是一个合理的启发式方法,因为文件时间戳通常只有在文件被修改时才会改变。但重要的是要意识到情况并非总是如此。例如,你可以修改一个文件,然后将该文件的修改时间戳更改为旧的。如果你这样做了,Make 会错误地猜测文件没有更改,因此可能会被忽略。
多嘴一句: 确保你理解这一点。它是 Makefile 的关键,可能需要你几分钟才能正确理解.如果事情仍然令人困惑,请尝试上述示例或观看上面的视频。